// encoding=utf-8

#include <QShortcut>
#include <QFile>
#include <QTextCodec>
#include <QVariant>
#include <QMap>
#include <QList>
#include <QSettings>
#include <QFont>
#include <QDebug>
#include <QySciLexer>

#include "QySciEdit.h"

/**************************************************************************************************/

inline QString extractEncoding( QString str ) {

	static const QString keyword("encoding");
	int index = str.toLower().indexOf(keyword);

	if ( index < 0 )
		return QString();

	index = str.count() - ( index + keyword.count() + 1 );
	str = str.right(index).simplified();

	return str.section( ' ', 0, 0 );
}

inline bool isTrueEncoding( const QString & name ) {

	if ( name.isEmpty() )
		return false;

	return QTextCodec::availableCodecs().contains( name.toAscii() );
}

/**************************************************************************************************/

class QyScintillaMarginMarker {
public:
	QyScintillaMarginMarker( QsciScintilla * scintilla, QsciScintilla::MarkerSymbol symbol, int id );
	~QyScintillaMarginMarker();

	int position() const { return p_position; }
	QsciScintilla * scintilla() const { return p_scintilla; }

	void setPosition( int position );
	void setForeground( const QColor & color );
	void setBackground( const QColor & color );

private:
	QsciScintilla * p_scintilla;
	int markerHandle, p_id, p_position;

};


QyScintillaMarginMarker::QyScintillaMarginMarker( QsciScintilla * scintilla,
	QsciScintilla::MarkerSymbol symbol, int id )
{

	p_scintilla = scintilla;
	p_id = id;
	markerHandle = 0;
	p_position = -1;

	if (p_scintilla)
		p_scintilla->markerDefine( symbol, p_id );

}

QyScintillaMarginMarker::~QyScintillaMarginMarker() {

	if ( markerHandle > 0 )
		p_scintilla->markerDeleteHandle(markerHandle);

}

void QyScintillaMarginMarker::setPosition( int position ) {

	Q_ASSERT(p_scintilla);

	p_position = position;

	if ( markerHandle > 0 )
		p_scintilla->markerDeleteHandle(markerHandle);

	markerHandle = p_scintilla->markerAdd( p_position, p_id );

}

void QyScintillaMarginMarker::setForeground( const QColor & color ) {

	Q_ASSERT(p_scintilla);

	p_scintilla->setMarkerForegroundColor( color, p_id );

}

void QyScintillaMarginMarker::setBackground( const QColor & color ) {

	Q_ASSERT(p_scintilla);

	p_scintilla->setMarkerBackgroundColor( color, p_id );

}
/*
QyScintillaMarginMarker * myMarker;

...

myMarker = new QyScintillaMarginMarker( myScintilla, QsciScintilla::RightArrow, 0 );

myMarker->setBackground( QColor("#ff9c00") );
myMarker->setForeground( QColor("#181818") );

...

myMarker->setPosition(50);

...

myMarker->setPosition(10);
*/


/**************************************************************************************************/

void QyMarginArrow::setPosition( int line ) {

	line --;

	if ( p_line == line && p_handle )
		return;

	if (p_handle)
		p_edit->markerDeleteHandle(p_handle);

	p_line = line;

	if ( p_line >= 0 )
		return;

	p_handle = p_edit->markerAdd( p_line, QySciMarkerType::Arrow );
	p_edit->ensureLineVisible(p_line);

}

void QyMarginArrow::setVisible( bool visible ) {

	if (visible)
		show();
	else
		hide();

}

void QyMarginArrow::show() {

	p_handle = p_edit->markerAdd( p_line, QySciMarkerType::Arrow );

}

void QyMarginArrow::hide() {

	if (p_handle) {

		p_edit->markerDeleteHandle(p_handle);
		p_handle = 0;

	}

}

QyMarginArrow::QyMarginArrow( QySciEdit * edit, int line ) {

	Q_ASSERT(edit);

	p_edit = edit;
	p_line = line;
	p_handle = 0;

}

/**************************************************************************************************/

class QySciEditSettings {
public:
	static void loadSettings( QSettings & qs, const QString & prefix );
	static void applySettings( QySciEdit * edit );

	static QMap<QString,QVariant> p_settings;
	static QList<QySciEdit*> p_editors;

};

QMap<QString,QVariant> QySciEditSettings::p_settings;
QList<QySciEdit*> QySciEditSettings::p_editors;

void QySciEditSettings::loadSettings( QSettings & qs, const QString & prefix ) {

	QStringList keys( p_settings.keys() );

	// qDebug() << "all keys" << qs.allKeys();

	foreach ( QString key, keys ) {

		if ( qs.contains( prefix + key ) ) {

			p_settings[key] = qs.value( prefix + key );

		}

		// qDebug() << "value [" << ( qs.contains( prefix + key ) ? "readed" : "default" ) << "]" << prefix + key << "=" << p_settings[key].toString() << p_settings[key].isValid();

	}

}

#define __TESTANDSETCOLOR(name,func) if ( p_settings[name].isValid() ) edit->func( QColor( p_settings[name].toString() ) )

void QySciEditSettings::applySettings( QySciEdit * edit ) {

	// qDebug() << "apply settings for" << edit;

	if (!edit)
		return;

	if ( p_settings.isEmpty() ) {

		p_settings["/arrow/back"] = "#00aa00";
		p_settings["/arrow/fore"] = "#ffffff";
		p_settings["/error/back"] = "#ff0000";
		p_settings["/error/fore"] = "#ffffff";
		p_settings["/warning/back"] = "#dddd00";
		p_settings["/warning/fore"] = "#ffffff";
		p_settings["/bookmark/back"] = "#0000dd";
		p_settings["/bookmark/fore"] = "#ffffff";
		p_settings["/caret/fore"] = QVariant();
		p_settings["/caret/back"] = QVariant();
		p_settings["/selection/fore"] = QVariant();
		p_settings["/selection/back"] = QVariant();
		p_settings["/fore"] = QVariant();
		p_settings["/back"] = QVariant();
		p_settings["/calltipsbackgroundcolor"] = QVariant();
		p_settings["/calltipsforegroundcolor"] = QVariant();
		p_settings["/calltipshighlightcolor"] = QVariant();
		p_settings["/marginsbackgroundcolor"] = QVariant();
		p_settings["/marginsforegroundcolor"] = QVariant();
		p_settings["/foldmargincolors"] = QVariant();
		p_settings["/indentationguidesbackgroundcolor"] = QVariant();
		p_settings["/indentationguidesforegroundcolor"] = QVariant();
		p_settings["/matchedbracebackgroundcolor"] = QVariant();
		p_settings["/matchedbraceforegroundcolor"] = QVariant();
		p_settings["/unmatchedbracebackgroundcolor"] = QVariant();
		p_settings["/unmatchedbraceforegroundcolor"] = QVariant();
		p_settings["/edgecolor"] = QVariant();

		p_settings["/indentationwidth"] = 4;
		p_settings["/tabwidth"] = 4;
		p_settings["/edgecolumn"] = 100;
		p_settings["/autocompletionthreshold"] = 3;
		p_settings["/caret/width"] = 1;
		p_settings["/caret/alpha"] = QVariant();

		p_settings["/caretlinevisible"] = true;

		p_settings["/font"] = QVariant();

	}

	edit->setMarkerBackgroundColor( QColor( p_settings["/arrow/back"].toString() ), QySciMarkerType::Arrow );
	edit->setMarkerForegroundColor( QColor( p_settings["/arrow/fore"].toString() ), QySciMarkerType::Arrow );
	edit->setMarkerBackgroundColor( QColor( p_settings["/error/back"].toString() ), QySciMarkerType::Error );
	edit->setMarkerForegroundColor( QColor( p_settings["/error/fore"].toString() ), QySciMarkerType::Error );
	edit->setMarkerBackgroundColor( QColor( p_settings["/warning/back"].toString() ), QySciMarkerType::Warning );
	edit->setMarkerForegroundColor( QColor( p_settings["/warning/fore"].toString() ), QySciMarkerType::Warning );
	edit->setMarkerBackgroundColor( QColor( p_settings["/bookmark/back"].toString() ), QySciMarkerType::Bookmark );
	edit->setMarkerForegroundColor( QColor( p_settings["/bookmark/fore"].toString() ), QySciMarkerType::Bookmark );

	__TESTANDSETCOLOR( "/caret/fore", setCaretForegroundColor );
	__TESTANDSETCOLOR( "/caret/back", setCaretLineBackgroundColor );
	__TESTANDSETCOLOR( "/selection/fore", setSelectionForegroundColor );
	__TESTANDSETCOLOR( "/selection/back", setSelectionBackgroundColor );
	__TESTANDSETCOLOR( "/fore", setColor );
	__TESTANDSETCOLOR( "/back", setPaper );
	__TESTANDSETCOLOR( "/calltipsbackgroundcolor", setCallTipsBackgroundColor );
	__TESTANDSETCOLOR( "/calltipsforegroundcolor", setCallTipsForegroundColor );
	__TESTANDSETCOLOR( "/calltipshighlightcolor", setCallTipsHighlightColor );
	__TESTANDSETCOLOR( "/marginsbackgroundcolor", setMarginsBackgroundColor );
	__TESTANDSETCOLOR( "/marginsforegroundcolor", setMarginsForegroundColor );
	if ( p_settings["/foldmargincolors"].isValid() ) {

		QStringList colors( p_settings["/foldmargincolors"].toStringList() );

		if ( colors.count() == 2 )
			edit->setFoldMarginColors( QColor(colors[0]), QColor(colors[1]) );

	}
	__TESTANDSETCOLOR( "/indentationguidesbackgroundcolor", setIndentationGuidesBackgroundColor );
	__TESTANDSETCOLOR( "/indentationguidesforegroundcolor", setIndentationGuidesForegroundColor );
	__TESTANDSETCOLOR( "/matchedbracebackgroundcolor", setMatchedBraceBackgroundColor );
	__TESTANDSETCOLOR( "/matchedbraceforegroundcolor", setMatchedBraceForegroundColor );
	__TESTANDSETCOLOR( "/unmatchedbracebackgroundcolor", setUnmatchedBraceBackgroundColor );
	__TESTANDSETCOLOR( "/unmatchedbraceforegroundcolor", setUnmatchedBraceForegroundColor );
	__TESTANDSETCOLOR( "/edgecolor", setEdgeColor );

	edit->setIndentationWidth( p_settings["/indentationwidth"].toInt() );
	edit->setTabWidth( p_settings["/tabwidth"].toInt() );
	edit->setEdgeColumn( p_settings["/edgecolumn"].toInt() );
	edit->setAutoCompletionThreshold( p_settings["/autocompletionthreshold"].toInt() );
	edit->setCaretWidth( p_settings["/caret/width"].toInt() );

	if ( p_settings["/caret/alpha"].isValid() && p_settings["/caret/back"].isValid() ) {

		QColor color( p_settings["/caret/back"].toString() );
		QColor newColor( color.red(), color.green(), color.blue(), p_settings["/caret/alpha"].toInt() );

		edit->setCaretLineBackgroundColor(newColor);

		// qDebug() << "Set sel bg color alpha" << newColor.alpha();
		// qDebug() << "Set sel bg color" << newColor.name() << newColor.rgba();

	}

	edit->setCaretLineVisible( p_settings["/caretlinevisible"].toBool() );

	if ( p_settings["/font"].isValid() ) {

		QStringList fontDescription( p_settings["/font"].toStringList() );

		if ( fontDescription.count() == 5 ) {

			QFont font;

			font.setFamily(fontDescription[0]);
			font.setPointSize( fontDescription[1].toInt() );
			font.setBold( fontDescription[2].toInt() );
			font.setItalic( fontDescription[3].toInt() );
			font.setUnderline( fontDescription[4].toInt() );

			edit->setFont(font);

		}

	}

}

/**************************************************************************************************/

QySciEdit::QySciEdit( QWidget * parent, const QString & name )
	: QsciScintilla(parent), p_marginArrow(this)
{

	setObjectName(name);
	init();

}

QySciEdit::QySciEdit( const QString & fileName, QWidget * parent, const QString & name )
	: QsciScintilla(parent), p_marginArrow(this)
{

	setObjectName(name);
	init();
	openFile(fileName);

}

QySciEdit::~QySciEdit() {

	QySciEditSettings::p_editors.removeOne(this);

}

int QySciEdit::line() const {

	int l, i;
	getCursorPosition( &l, &i );

	return l;
}

int QySciEdit::index() const {

	int l, i;
	getCursorPosition( &l, &i );

	return i;
}

bool QySciEdit::findFirst( const QString & str, bool re, bool cs, bool wm, bool wrap,
	bool forward, int line, int index, bool show )
{

	// обрабатываем запрос на поиск посвоему, проверяем, начинали ли мы уже поиск в данном редакторе, если нет - начинаем, если да - продорлжаем

	Q_UNUSED(wrap);
	Q_UNUSED(forward);
	Q_UNUSED(show);

	return findText( QySciFindRequest( str, re, cs, wm ), line, index );

}

bool QySciEdit::findText( QySciFindRequest request, int line, int index ) {

	if ( !request.isValid() ) {

		request = lastRequest;

		if ( !request.isValid() )
			return false;

	}

	int l, i;

	getCursorPosition( &l, &i );

	if ( request == lastRequest && line < 0 && index < 0 ) {

		// продолжить посик

		return QsciScintilla::findFirst( request.text(), request.regExp(), request.caseSensitive(),
			request.wordMatches(), true, true, l, i );
		// return QsciScintilla::findNext();
	}

	// инициализировать новый поиск, начиная от текущей позиции

	lastRequest = request;

	return QsciScintilla::findFirst( request.text(), request.regExp(), request.caseSensitive(),
		request.wordMatches(), true, true, l, i );
}

bool QySciEdit::findText( const QString & str, bool re, bool cs, bool wm, int line, int index ) {

	return findText( QySciFindRequest( str, re, cs, wm ), line, index );
}

bool QySciEdit::replaceOne( const QString & findText, const QString & replaceText, bool re,
	bool cs, bool wm )
{

	int currentLine, currentIndex;
	bool ok = false;

	getCursorPosition( &currentLine, &currentIndex );

	if ( hasSelectedText() ) {

		int startLine, startIndex, endLine, endLineIndex;

		getSelection( &startLine, &startIndex, &endLine, &endLineIndex );
		setCursorPosition( startLine, startIndex );

		currentLine = startLine;
		currentIndex = startIndex;

	}

	if ( ok = QsciScintilla::findFirst( findText, re, cs, wm, true, true, currentLine,
		currentIndex, false ) )
	{
		replace(replaceText);
	}

	return ok;
}

bool QySciEdit::replaceAll( const QString & findText, const QString & replaceText, bool re,
	bool cs, bool wm )
{

	int i = 0;

	beginUndoAction();

	while ( replaceOne( findText, replaceText, re, cs, wm ) )
		i++;

	endUndoAction();

	return ( i > 0 );
}

bool QySciEdit::replaceInSelection( const QString & findText, const QString & replaceText,
	bool re, bool cs, bool wm )
{

	Q_UNUSED(re);
	Q_UNUSED(wm);

	QString selecetdText( QySciEdit::selectedText() );

	qDebug() << "selecetdText" << selecetdText;

	selecetdText.replace( findText, replaceText, cs ? Qt::CaseSensitive : Qt::CaseInsensitive );

	qDebug() << "result" << selecetdText;

	beginUndoAction();
	removeSelectedText();
	insert(selecetdText);
	endUndoAction();

	return true;
}

QString QySciEdit::fileEncoding( const QString & fileName ) {

	QFile file(fileName);
	QString encoding;

	if ( file.open(QFile::ReadOnly) ) {

		encoding = extractEncoding( file.readLine() );

		if ( encoding.isEmpty() )
			encoding = extractEncoding( file.readLine() );

		file.close();

	}

	return encoding;
}

void QySciEdit::openFile( const QString & fileName, const QString & encodingName ) {

	// qDebug() << "p_encodingName" << p_encodingName;
	// qDebug() << "encodingName" << encodingName;

	if ( p_lastFileName != fileName || p_encodingName != encodingName ) {

		// qDebug() << "opening ...";

		QFile file(fileName);
		if ( file.exists() && file.open(QFile::ReadOnly) ) {

			QString encoding = fileEncoding(fileName);

			// qDebug() << "encoding" << encoding;

			if ( isTrueEncoding(encoding) )
				setEncoding(encoding);
			else if ( isTrueEncoding(encodingName) )
				setEncoding(encodingName);
			else if ( !isTrueEncoding(p_encodingName) )
				setEncoding( QTextCodec::codecForLocale()->name() );

			// qDebug() << "encodingName()" << QySciEdit::encodingName();

			if ( QTextCodec * codec = QTextCodec::codecForName( p_encodingName.toAscii() ) )
				setText( codec->toUnicode( file.readAll() ) );
			else
				read(&file);

			file.close();
			// foldAll();
			setModified(false);

		}

		if ( p_lastFileName != fileName )
			fileNameChanged(fileName);

		p_lastFileName = fileName;

	}

}

void QySciEdit::openFile( const QString & fileName, int line ) {

	line--;

	if ( p_lastFileName != fileName ) {

		QFile file(fileName);
		if ( file.exists() && file.open(QFile::ReadOnly) ) {

			QString encoding = fileEncoding(fileName);

			if ( isTrueEncoding(encoding) )
				setEncoding(encoding);
			else if ( !isTrueEncoding(p_encodingName) )
				setEncoding( QTextCodec::codecForLocale()->name() );

			if ( QTextCodec * codec = QTextCodec::codecForName( p_encodingName.toAscii() ) )
				setText( codec->toUnicode( file.readAll() ) );
			else
				read(&file);

			file.close();
			// foldAll();
			setModified(false);

		}

		if ( p_lastFileName != fileName )
			fileNameChanged(fileName);

		p_lastFileName = fileName;

	}

	if ( line >= 0 ) {

		p_marginArrow.setPosition(-1);
		setCursorPosition( line, 0 );
		ensureLineVisible(line);

	}

}

bool QySciEdit::save( QString fileName, const QString & encodingName ) {

	if ( fileName.isEmpty() )
		fileName = p_lastFileName;

	if ( fileName.isEmpty() )
		return false;

	QFile file(fileName);

	if ( file.open(QFile::WriteOnly) ) {

		QString encoding = extractEncoding( text(0) );
		if ( encoding.isEmpty() )
			encoding = extractEncoding( text(1) );

		if ( isTrueEncoding(encoding) )
			setEncoding(encoding);
		else if ( isTrueEncoding(encodingName) )
			setEncoding(encodingName);

		if (p_autoStripTrailingSpaces)
			stripTrailingSpaces();

		if ( QTextCodec * codec = QTextCodec::codecForName( p_encodingName.toAscii() ) )
			file.write( codec->fromUnicode( text() ) );
		else
			write(&file);

		file.close();

		if ( p_lastFileName != fileName )
			emit fileNameChanged(fileName);

		p_lastFileName = fileName;

		setModified(false);

		return true;
	}

	return false;
}

void QySciEdit::addMarker( int line, int markerType ) {

	markerAdd( line, markerType );

}

void QySciEdit::removeMarkers( int line, int markerType ) {

	if ( line < 0 )
		markerDeleteAll(markerType);
	else
		markerDelete( line, markerType );

}

void QySciEdit::leftShiftCurrent() {

	int index, startLine, currentIndex, endLine, currentLine;

	selectAllCurrentLines();
	getSelection( &startLine, &currentIndex, &endLine, &index );

	beginUndoAction();

	for ( currentLine=startLine; currentLine<endLine; currentLine ++ )
		unindent(currentLine);

	endUndoAction();

}

void QySciEdit::foldCurrent() {

	foldLine( line() );

}

void QySciEdit::stripTrailingSpaces() {

	int maxLines = SendScintilla(SCI_GETLINECOUNT), line;

	for ( line=0; line<maxLines; line++ ) {

		int lineStart = SendScintilla( SCI_POSITIONFROMLINE, line );
		int lineEnd = SendScintilla( SCI_GETLINEENDPOSITION, line );
		int i = lineEnd - 1;
		char ch = static_cast<char>( SendScintilla( SCI_GETCHARAT, i ) );

		while ( ( i >= lineStart ) && ( ( ch == ' ' ) || ( ch == '\t' ) ) ) {

			i--;
			ch = static_cast<char>( SendScintilla( SCI_GETCHARAT, i ) );

		}

		if ( i < ( lineEnd - 1 ) ) {

			SendScintilla( SCI_SETTARGETSTART, i + 1 );
			SendScintilla( SCI_SETTARGETEND, lineEnd );
			SendScintilla( SCI_REPLACETARGET, (char*)0, "" );

		}

	}

}

void QySciEdit::selectAllCurrentLines() {

	int startLine = -1, endLine = -1, endLineIndex = -1, currentIndex;

	if ( hasSelectedText() ) {
		getSelection( &startLine, &currentIndex, &endLine, &endLineIndex );
	} else {
		getCursorPosition( &startLine, &currentIndex );
	}

	if (startLine) {

		if ( endLine < 0 )
			endLine = startLine;

		if ( endLineIndex == 0 )
			endLine --;

		setSelection ( startLine, 0, endLine, trueLineLength(endLine) );

	}

}

void QySciEdit::setLine( int line ) {

	setCursorPosition( line, 0 );

}

inline QString GetRange( QsciScintilla * scintilla, int selStart, int selEnd ) {

	struct CharacterRange {
		long cpMin;
		long cpMax;
	};
	struct TextRange {
		struct CharacterRange chrg;
		char * lpstrText;
	};

	QByteArray sel( selEnd - selStart, '\0' );
	TextRange tr;

	tr.chrg.cpMin = selStart;
	tr.chrg.cpMax = selEnd;
	tr.lpstrText = sel.data();

	scintilla->SendScintilla( QsciScintilla::SCI_GETTEXTRANGE, 0, &tr );

	return QString(sel);
}

void QySciEdit::toggleCommentSelectedLines() {

	QySciLexer * sciLexer = dynamic_cast<QySciLexer *>( lexer() );

	// qDebug() << sciLexer;

	if (!sciLexer)
		return;

	QString comment = sciLexer->lineComment();
	bool placeCommentsAtLineStart = true;

	// qDebug() << "comment" << comment;

	if ( comment.isEmpty() )
		return;

	QString longComment = comment + " ";

	int selectionStart = SendScintilla(SCI_GETSELECTIONSTART);
	int selectionEnd = SendScintilla(SCI_GETSELECTIONEND);
	int caretPosition = SendScintilla(SCI_GETCURRENTPOS);

	// checking if caret is located in _beginning_ of selected block
	bool move_caret = caretPosition < selectionEnd;
	int selStartLine = SendScintilla( SCI_LINEFROMPOSITION, selectionStart );
	int selEndLine = SendScintilla( SCI_LINEFROMPOSITION, selectionEnd );
	int lines = selEndLine - selStartLine;
	int firstSelLineStart = SendScintilla( SCI_POSITIONFROMLINE, selStartLine );
	// "caret return" is part of the last selected line
	if ( ( lines > 0 ) &&
	        ( selectionEnd == SendScintilla(SCI_POSITIONFROMLINE, selEndLine) ) )
	{
		selEndLine--;
	}

	SendScintilla(SCI_BEGINUNDOACTION);

	for ( int i=selStartLine; i<=selEndLine; i++ ) {

		int lineStart = SendScintilla( SCI_POSITIONFROMLINE, i );
		int lineIndent = lineStart;
		int lineEnd = SendScintilla( SCI_GETLINEENDPOSITION, i );

		if (!placeCommentsAtLineStart) {
			lineIndent = SendScintilla( SCI_GETLINEINDENTPOSITION, i );
		}

		QString linebuf = GetRange( this, lineIndent, lineEnd );

		// empty lines are not commented
		if ( linebuf.length() < 1 )
			continue;

		if ( linebuf.startsWith(comment) ) {

			int commentLength = comment.length();

			if ( linebuf.startsWith(longComment) ) {
				// Removing comment with space after it.
				commentLength = longComment.length();
			}

			const char emptyCStr [] = "";
			SendScintilla( SCI_SETSEL, lineIndent, lineIndent + commentLength );
			SendScintilla( (uint) SCI_REPLACESEL, (long unsigned int) 0, emptyCStr );

			if ( i == selStartLine ) // is this the first selected line?
				selectionStart -= commentLength;

			selectionEnd -= commentLength; // every iteration

			continue;
		}

		if (i == selStartLine) // is this the first selected line?
			selectionStart += longComment.length();

		selectionEnd += longComment.length(); // every iteration
		SendScintilla( SCI_INSERTTEXT, lineIndent, longComment.toStdString().c_str() );

	}

	// after uncommenting selection may promote itself to the lines
	// before the first initially selected line;
	// another problem - if only comment symbol was selected;
	if ( selectionStart < firstSelLineStart ) {

		if (selectionStart >= selectionEnd - longComment.length() - 1 )
			selectionEnd = firstSelLineStart;

		selectionStart = firstSelLineStart;

	}

	if (move_caret) {
		// moving caret to the beginning of selected block
		SendScintilla( SCI_GOTOPOS, selectionEnd );
		SendScintilla( SCI_SETCURRENTPOS, selectionStart );

	} else {
		SendScintilla( SCI_SETSEL, selectionStart, selectionEnd );
	}

	SendScintilla(SCI_ENDUNDOACTION);

}

void QySciEdit::loadSettings( QSettings & qs, const QString & prefix ) {

	QySciEditSettings::loadSettings( qs, prefix );

	foreach ( QySciEdit * edit, QySciEditSettings::p_editors )
		QySciEditSettings::applySettings(edit);

}

void QySciEdit::setEncoding( const QString & encodingName ) {

	p_encodingName = encodingName;

	emit encodingChanged(p_encodingName);

}

void QySciEdit::linesCountChanged() {

	setMarginWidth( 0, fontMetrics().width( QString::number( lines() ) ) + 10 );

}

void QySciEdit::init() {

	p_isOpen = false;
	p_isNew = true;
	p_autoStripTrailingSpaces = true;

	setUtf8(true);
	markerDefine( RightArrow, QySciMarkerType::Arrow );
	markerDefine( Circle, QySciMarkerType::Error );
	markerDefine( Circle, QySciMarkerType::Warning );
	markerDefine( Rectangle, QySciMarkerType::Bookmark );


	linesCountChanged();
	setMarginWidth( 1, 25 );
	setMarginWidth( 2, 30 );
	setMarginLineNumbers( 0, true );
	setMarginSensitivity( 1, true );
	setFolding(QsciScintilla::BoxedTreeFoldStyle);
	setBackspaceUnindents(true);
	setTabIndents(true);
	setAutoIndent(true);
	setIndentationGuides(true);
	setIndentationsUseTabs(true);
	setBraceMatching(QsciScintilla::SloppyBraceMatch);
	setAutoCompletionShowSingle(true);
	setBackspaceUnindents(true);
	setAutoCompletionFillupsEnabled(true);

	setWrapMode(QsciScintilla::WrapWord);
	setEdgeMode(QsciScintilla::EdgeLine);

	QySciEditSettings::p_editors.append(this);
	QySciEditSettings::applySettings(this);

	connect( this, SIGNAL( linesChanged() ), this, SLOT( linesCountChanged() ) );

	setEncoding( QTextCodec::codecForLocale()->name() );

	new QShortcut( QKeySequence("Shift+Tab"), this, SLOT( leftShiftCurrent() ) );
	new QShortcut( QKeySequence("Ctrl+I"), this, SLOT( callTip() ) );
	new QShortcut( QKeySequence("Ctrl+Space"), this, SLOT( autoCompleteFromAll() ) );

}
